<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.3.0">


  <link rel="apple-touch-icon" sizes="180x180" href="/yuwanzi.io/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/yuwanzi.io/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/yuwanzi.io/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/yuwanzi.io/images/logo.svg" color="#222">

<link rel="stylesheet" href="/yuwanzi.io/css/main.css">



<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.1/css/all.min.css">
  <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/animate.css@3.1.1/animate.min.css">

<script class="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"suyuhuan.gitee.io","root":"/yuwanzi.io/","images":"/yuwanzi.io/images","scheme":"Muse","version":"8.2.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":false,"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"Suche...","empty":"We didn't find any results for the search: ${query}","hits_time":"${hits} results found in ${time} ms","hits":"${hits} results found"}};
  </script>
<meta name="description" content="概述&amp;nbsp;&amp;nbsp;Storm是一个开源的分布式实时计算系统，可以简单、可靠的处理大量的数据流。被称作“实时的hadoop”。Storm有很多使用场景：如实时分析，在线机器学习，持续计算， 分布式RPC，ETL等等。Storm支持水平扩展，具有高容错性，保证每个消息都会得到处理，而且处理速度很快。Storm的部署和运维都很便捷，而且更为重要的是可以使用任意编程语言来开发应用。 Storm">
<meta property="og:type" content="article">
<meta property="og:title" content="Hadoop学习笔记(6)-Storm">
<meta property="og:url" content="https://suyuhuan.gitee.io/yuwanzi.io/2016/07/19/2016-07-19-Hadoop06-Storm/index.html">
<meta property="og:site_name" content="玉丸子 | Blog">
<meta property="og:description" content="概述&amp;nbsp;&amp;nbsp;Storm是一个开源的分布式实时计算系统，可以简单、可靠的处理大量的数据流。被称作“实时的hadoop”。Storm有很多使用场景：如实时分析，在线机器学习，持续计算， 分布式RPC，ETL等等。Storm支持水平扩展，具有高容错性，保证每个消息都会得到处理，而且处理速度很快。Storm的部署和运维都很便捷，而且更为重要的是可以使用任意编程语言来开发应用。 Storm">
<meta property="og:locale">
<meta property="og:image" content="http://ww3.sinaimg.cn/mw690/63503acbjw1f5z3iwds07j207g03nmxf.jpg">
<meta property="og:image" content="http://ww4.sinaimg.cn/mw690/63503acbjw1f5za3m6lqaj20go0f5gn1.jpg">
<meta property="og:image" content="http://ww4.sinaimg.cn/mw690/63503acbjw1f5za9p00fyj20mo0a975d.jpg">
<meta property="og:image" content="http://ww3.sinaimg.cn/mw690/63503acbjw1f5zdzh1eo9j20bq08h0t6.jpg">
<meta property="article:published_time" content="2016-07-19T10:00:00.000Z">
<meta property="article:modified_time" content="2020-11-07T00:58:17.000Z">
<meta property="article:author" content="玉丸子">
<meta property="article:tag" content="大数据">
<meta property="article:tag" content="Storm">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://ww3.sinaimg.cn/mw690/63503acbjw1f5z3iwds07j207g03nmxf.jpg">


<link rel="canonical" href="https://suyuhuan.gitee.io/yuwanzi.io/2016/07/19/2016-07-19-Hadoop06-Storm/">


<script class="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh'
  };
</script>
<title>Hadoop学习笔记(6)-Storm | 玉丸子 | Blog</title>
  




  <noscript>
  <style>
  body { margin-top: 2rem; }

  .use-motion .menu-item,
  .use-motion .sidebar,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header {
    visibility: visible;
  }

  .use-motion .header,
  .use-motion .site-brand-container .toggle,
  .use-motion .footer { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle,
  .use-motion .custom-logo-image {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line {
    transform: scaleX(1);
  }

  .search-pop-overlay, .sidebar-nav { display: none; }
  .sidebar-panel { display: block; }
  </style>
</noscript>

<link rel="alternate" href="/yuwanzi.io/atom.xml" title="玉丸子 | Blog" type="application/atom+xml">
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="Navigationsleiste an/ausschalten" role="button">
    </div>
  </div>

  <div class="site-meta">

    <a href="/yuwanzi.io/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">玉丸子 | Blog</h1>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>







</div>
        
  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>

  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          Inhaltsverzeichnis
        </li>
        <li class="sidebar-nav-overview">
          Übersicht
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%A6%82%E8%BF%B0"><span class="nav-number">1.</span> <span class="nav-text">概述</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Storm%E7%9A%84%E7%89%B9%E7%82%B9"><span class="nav-number">2.</span> <span class="nav-text">Storm的特点</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Storm%E6%9E%B6%E6%9E%84"><span class="nav-number">3.</span> <span class="nav-text">Storm架构</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#Stream"><span class="nav-number">3.1.</span> <span class="nav-text">Stream</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#Topologies"><span class="nav-number">3.2.</span> <span class="nav-text">Topologies</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#Spouts-amp-Bolts"><span class="nav-number">3.3.</span> <span class="nav-text">Spouts&amp;Bolts</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#Stream-Groupings"><span class="nav-number">3.4.</span> <span class="nav-text">Stream Groupings</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#Workers"><span class="nav-number">3.5.</span> <span class="nav-text">Workers</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#Task"><span class="nav-number">3.6.</span> <span class="nav-text">Task</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Storm%E5%AE%89%E8%A3%85%E9%83%A8%E7%BD%B2"><span class="nav-number">4.</span> <span class="nav-text">Storm安装部署</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9"><span class="nav-number">4.1.</span> <span class="nav-text">注意事项</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4"><span class="nav-number">5.</span> <span class="nav-text">常用命令</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E6%8F%90%E4%BA%A4Topologies"><span class="nav-number">5.1.</span> <span class="nav-text">提交Topologies</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%81%9C%E6%AD%A2Topologies"><span class="nav-number">5.2.</span> <span class="nav-text">停止Topologies</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#API"><span class="nav-number">6.</span> <span class="nav-text">API</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#Spouts"><span class="nav-number">6.1.</span> <span class="nav-text">Spouts</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#Bolts"><span class="nav-number">6.2.</span> <span class="nav-text">Bolts</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#Topology-Example"><span class="nav-number">6.3.</span> <span class="nav-text">Topology Example</span></a></li></ol></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">玉丸子</p>
  <div class="site-description" itemprop="description">这里是玉丸子的个人博客,与你一起发现更大的世界。</div>
</div>
<div class="site-state-wrap site-overview-item animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/yuwanzi.io/archives">
          <span class="site-state-item-count">68</span>
          <span class="site-state-item-name">Artikel</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/yuwanzi.io/categories/">
        <span class="site-state-item-count">39</span>
        <span class="site-state-item-name">Kategorien</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/yuwanzi.io/tags/">
        <span class="site-state-item-count">46</span>
        <span class="site-state-item-name">schlagwörter</span></a>
      </div>
  </nav>
</div>



        </div>
      </div>
    </div>
  </aside>
  <div class="sidebar-dimmer"></div>


    </header>

    
  <div class="back-to-top" role="button">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2016/07/19/2016-07-19-Hadoop06-Storm/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          Hadoop学习笔记(6)-Storm
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2016-07-19 18:00:00" itemprop="dateCreated datePublished" datetime="2016-07-19T18:00:00+08:00">2016-07-19</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">后端</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/%E5%A4%A7%E6%95%B0%E6%8D%AE/" itemprop="url" rel="index"><span itemprop="name">大数据</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/%E5%A4%A7%E6%95%B0%E6%8D%AE/Storm/" itemprop="url" rel="index"><span itemprop="name">Storm</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
        <p><img src="http://ww3.sinaimg.cn/mw690/63503acbjw1f5z3iwds07j207g03nmxf.jpg"></p>
<h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><p>&nbsp;&nbsp;Storm是一个开源的分布式实时计算系统，可以简单、可靠的处理大量的数据流。被称作“实时的hadoop”。Storm有很多使用场景：如实时分析，在线机器学习，持续计算， 分布式RPC，ETL等等。Storm支持水平扩展，具有高容错性，保证每个消息都会得到处理，而且处理速度很快。Storm的部署和运维都很便捷，而且更为重要的是可以使用任意编程语言来开发应用。</p>
<h3 id="Storm的特点"><a href="#Storm的特点" class="headerlink" title="Storm的特点"></a>Storm的特点</h3><p><strong>简单的编程模型</strong></p>
<p>&nbsp;&nbsp;在大数据处理方面相信大家对hadoop已经耳熟能详，基于Google Map/Reduce来实现的Hadoop为开发者提供了map、reduce原语，使并行批处理程序变得非常地简单和优美。</p>
<p>&nbsp;&nbsp;同样，Storm也为大数据 的实时计算提供了一些简单优美的原语，这大大降低了开发并行实时处理的任务的复杂性，帮助你快速、高效的开发应用。</p>
<p><strong>水平扩展</strong></p>
<p>&nbsp;&nbsp;在Storm集群中真正运行topology的主要有三个实体：工作进程、线程和任务。Storm集群中的每台机器上都可以运行多个工作进程，每个 工作进程又可创建多个线程，每个线程可以执行多个任务，任务是真正进行数据处理的实体，我们开发的spout、bolt就是作为一个或者多个任务的方式执行的。</p>
<p>&nbsp;&nbsp;计算任务在多个线程、进程和服务器之间并行进行，支持灵活的水平扩展。</p>
<p><strong>支持多种编程语言</strong></p>
<p>&nbsp;&nbsp;你可以在Storm之上使用各种编程语言。默认支持Clojure、Java、Ruby和Python。要增加对其他语言的支持，只需实现一个简单的Storm通信协议即可。</p>
<p><strong>高可靠性</strong></p>
<p>&nbsp;&nbsp;Storm保证每个消息至少能得到一次完整处理。任务失败时，它会负责从消息源重试消息。</p>
<p>&nbsp;&nbsp;spout发出的消息后续可能会触发产生成千上万条消息，可以形象的理解为一棵消息树，其中spout发出的消息为树根，Storm会跟踪这棵消息树的处理情况，只有当这棵消息树中的所有消息都被处理了，Storm才会认为spout发出的这个消息已经被“完全处理”。如果这棵消息树中的任何一个消息处理失败了，或者整棵消息树在限定的时间内没有“完全处理”，那么spout发出的消息就会重发。</p>
<p><strong>高容错性</strong></p>
<p>&nbsp;&nbsp;Storm会管理工作进程和节点的故障。</p>
<p>&nbsp;&nbsp;如果在消息处理过程中出了一些异常，Storm会重新安排这个出问题的处理单元。Storm保证一个处理单元永远运行（除非你显式杀掉这个处理单元）。</p>
<p>&nbsp;&nbsp;当然，如果处理单元中存储了中间状态，那么当处理单元重新被Storm启动的时候，需要应用自己处理中间状态的恢复。</p>
<p><strong>本地模式</strong></p>
<p>&nbsp;&nbsp;Storm有一个“本地模式”，可以在处理过程中完全模拟Storm集群。这让你可以快速进行开发和单元测试。</p>
<h3 id="Storm架构"><a href="#Storm架构" class="headerlink" title="Storm架构"></a>Storm架构</h3><p><img src="http://ww4.sinaimg.cn/mw690/63503acbjw1f5za3m6lqaj20go0f5gn1.jpg"></p>
<p>&nbsp;&nbsp;Storm集群由一个主节点和多个工作节点组成。主节点运行了一个名为“Nimbus”的守护进程，用于分配代码、布置任务及故障检测。每个工作节 点都运行了一个名为“Supervisor”的守护进程，用于监听工作，开始并终止工作进程。Nimbus和Supervisor都能快速失败，而且是无状态的，这样一来它们就变得十分健壮，两者的协调工作是由ApacheZooKeeper来完成的。</p>
<h4 id="Stream"><a href="#Stream" class="headerlink" title="Stream"></a>Stream</h4><p>&nbsp;&nbsp;Stream是一个数据流的抽象。这是一个没有边界的Tuple序列,而这些Tuple序列会以一种分布式的方式并行地创建和处理。</p>
<p>&nbsp;&nbsp;对消息流的定义主要就是对消息流里面的tuple 进行定义，为了更好地使用tuple，需要给tuple 里的每个字段取一个名字，并且不同的tuple 字段对应的类型要相同，即两个tuple 的第一个字段类型相同，第二个字段类型相同，但是第一个字段和第二个字段的类型可以不同。默认情况下，tuple 的字段类型可以为integer、long、short、byte、string、double、float、boolean 和byte array 等基本类型，也可以自定义类型，只需要实现相应的序列化接口。</p>
<p>&nbsp;&nbsp;每一个消息流在定义的时候需要被分配一个id，最常见的消息流是单向的消息流，在Storm 中OutputFieldsDeclarer 定义了一些方法，让你可以定义一个Stream 而不用指定这个id。在这种情况下，这个Stream 会有个默认的id: 1。</p>
<h4 id="Topologies"><a href="#Topologies" class="headerlink" title="Topologies"></a>Topologies</h4><p>&nbsp;&nbsp;<strong>Topology是由Stream Grouping连接起来的Spout和Bolt节点网络。</strong></p>
<p>&nbsp;&nbsp;在 Storm 中，一个实时计算应用程序的逻辑被封装在一个称为Topology 的对象中，也称为计算拓扑。Topology 有点类似于Hadoop 中的MapReduce Job，但是它们之间的关键区别在于，一个MapReduce Job 最终总是会结束的，然而一个Storm 的Topology 会一直运行。在逻辑上，一个Topology 是由一些Spout（消息的发送者）和Bolt（消息的处理者）组成图状结构，而链接Spouts 和Bolts 的则是Stream Groupings。</p>
<h4 id="Spouts-amp-Bolts"><a href="#Spouts-amp-Bolts" class="headerlink" title="Spouts&amp;Bolts"></a>Spouts&amp;Bolts</h4><p><img src="http://ww4.sinaimg.cn/mw690/63503acbjw1f5za9p00fyj20mo0a975d.jpg"></p>
<p><strong>Spouts</strong></p>
<p>&nbsp;&nbsp;Spouts 是Storm集群中一个计算任务（Topology）中消息流的生产者，Spouts一般是从别的数据源（例如，数据库或者文件系统）加载数据，然后向Topology中发射消息。</p>
<p>&nbsp;&nbsp;Spouts即可以是可靠的,也可以是不可靠的。</p>
<p>&nbsp;&nbsp;在一个Topology中存在两种Spouts，一种是可靠的Spouts，一种是非可靠的Spouts，可靠的Spouts 在一个tuple 没有成功处理的时候会重新发射该tuple，以保证消息被正确地处理。不可靠的Spouts 在发射一个tuple 之后，不会再重新发射该tuple，即使该tuple 处理失败。每个Spouts 都可以发射多个消息流，要实现这样的效果，可以使用OutFieldsDeclarer.declareStream 来定义多个Stream，然后使用SpoutOutputCollector 来发射指定的Stream。</p>
<p>&nbsp;&nbsp;在Storm 的编程接口中，Spout 类最重要的方法是nextTuple()方法，使用该方法可以发射一个消息tuple 到Topology 中，或者简单地直接返回，如果没有消息要发射。需要注意的是，nextTuple 方法的实现不能阻塞Spout，因为Storm在同一线程上调用Spout 的所有方法。Spout 类的另外两个重要的方法是ack()和fail()，一个tuple 被成功处理完成后，ack()方法被调用，否则就调用fail()方法。注意，只有对于可靠的Spout，才会调用ack()和fail()方法。</p>
<p><strong>Bolts</strong></p>
<p>&nbsp;&nbsp;所有消息处理的逻辑都在Bolt 中完成，在Bolt 中可以完成如过滤、分类、聚集、计算、查询数据库等操作。Bolt 可以做简单的消息处理操作，例如，Bolt 可以不做任何操作，只是将接收到的消息转发给其他的Bolt。Bolt 也可以做复杂的消息流的处理，从而需要很多个Bolt。在实际使用中，一条消息往往需要经过多个处理步骤，例如，计算一个班级中成绩在前十名的同学，首先需要对所有同学的成绩进行排序，然后在排序过的成绩中选出前十名的<br>成绩的同学。所以在一个Topology 中，往往有很多个Bolt，从而形成了复杂的流处理网络。</p>
<p>&nbsp;&nbsp;Bolts可以发射多条消息流。</p>
<ol>
<li>使用OutputFieldsDeclarer.declareStream定义Stream。</li>
<li>使用OutputCollector.emit来选择要发射的Stream。</li>
</ol>
<p>&nbsp;&nbsp;Bolts的主要方法是execute。</p>
<p>&nbsp;&nbsp;Bolts以Tuple作为输入,使用OutputCollector来发射Tuple,通过调用OutputCollector.ack()通知这个Tuple的发射者Spout。</p>
<p>&nbsp;&nbsp;Bolts一般流程。</p>
<p>&nbsp;&nbsp;处理一个输入Tuple,发射0个或多个Tuple,然后调用ack()通知Storm自己已经处理过这个Tuple了。Storm提供了一个IBasicBolt会自动调用ack()。</p>
<h4 id="Stream-Groupings"><a href="#Stream-Groupings" class="headerlink" title="Stream Groupings"></a>Stream Groupings</h4><p>&nbsp;&nbsp;定义一个 Topology 的其中一步是定义每个Bolt 接收什么样的流作为输入。Stream Grouping 就是用来定义一个Stream 应该如何分配给Bolts 上面的多个Tasks。</p>
<p>&nbsp;&nbsp;Storm里有7种类型的Stream Grouping。</p>
<ol>
<li>Shuffle Grouping 随机分组,随机派发Stream里面的Tuple,保证每个Bolt接收到的Tuple数量大致相同。</li>
<li>Fields Grouping 按字段分组,以id举例。具有相同id的Tuple会被分到相同的Bolt中的一个Task,而不同id的Tuple会被分到不同的Bolt中的Task。</li>
<li>All Grouping 广播,对于每一个Tuple,所有的Bolts都会收到。</li>
<li>Global Grouping 全局分组,这个Tuple被分配到Storm中的一个Bolt的其中一个Task。具体一点就是分配给id值最低的那个Task。</li>
<li>Non Grouping 不分组,Stream不关心到底谁会收到它的Tuple。目前这种分组和Shuffle Grouping是一样的效果,有一点不同的是Storm会把这个Bolt放到这个Bolt的订阅者同一个线程中去执行。</li>
<li>Direct Grouping 直接分组,这是一种比较特别的分组方法,用这种分组意味着消息的发送者指定由消息接收者的哪个Task处理这个消息。只有被声明为Direct Stream的消息流可以声明这种分组方法。而且这种消息Tuple必须使用emitDirect方法来发射。消息处理者可以通过TopologyContext来获取处理它的消息的Task的id(OutputCollector.emit方法也会返回Task的id)。</li>
<li>Local or Shuffle Grouping 如果目标Bolt有一个或者多个Task在同一个工作进程中,Tuple将会被随机发射给这些Tasks。否则,和普通的Shuffle Grouping行为一致。</li>
</ol>
<h4 id="Workers"><a href="#Workers" class="headerlink" title="Workers"></a>Workers</h4><p><img src="http://ww3.sinaimg.cn/mw690/63503acbjw1f5zdzh1eo9j20bq08h0t6.jpg"></p>
<ol>
<li>每个Supervisor中运行着多个Workers进程。</li>
<li>每个Worker进程中运行着多个Executor线程。</li>
<li>每个Executor线程中运行着若干个相同的Task(Spout/Bolt)。</li>
</ol>
<p>&nbsp;&nbsp;一个 Topology 可能会在一个或者多个工作进程里面执行，每个工作进程执行整个Topology 的一部分。比如，对于并行度是300 的Topology 来说，如果我们使用50 个工作进程来执行，那么每个工作进程会处理其中的6 个Tasks（其实就是每个工作进程里面分配6 个线程）。Storm 会尽量均匀地把工作分配给所有的工作进程。</p>
<h4 id="Task"><a href="#Task" class="headerlink" title="Task"></a>Task</h4><p>&nbsp;&nbsp;在 Storm 集群上，每个Spout 和Bolt 都是由很多个Task 组成的，每个Task对应一个线程，流分组策略就是定义如何从一堆Task 发送tuple 到另一堆Task。在实现自己的Topology 时可以调用TopologyBuilder.setSpout() 和TopBuilder.setBolt()方法来设置并行度，也就是有多少个Task。</p>
<h3 id="Storm安装部署"><a href="#Storm安装部署" class="headerlink" title="Storm安装部署"></a>Storm安装部署</h3><ol>
<li>安装jdk。</li>
<li>搭建Zookeeper集群。</li>
<li>下载并解压Storm。</li>
<li>修改storm.yaml配置文件。<ul>
<li>storm.zookeeper.servers: Storm集群使用的Zookeeper集群地址。例如:<br>storm.zookeeper.servers:<br>-“192.168.145.141”<br>-“192.168.145.142”</li>
<li>如果Zookeeper没有使用默认端口,那么还需要修改storm.zookeeper.port。</li>
<li>storm.local.dir Nimbus和Supervisor进程用于存储少量状态,如jars、confs等的本地磁盘目录,需要提前创建该目录并给予足够的访问权限。然后在storm.yaml中配置该目录,例如:<br>storm.local.dir:”/home/application/storm/workdir”</li>
</ul>
</li>
</ol>
<h4 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h4><p>&nbsp;&nbsp;启动Storm后台进程时,需要对conf/storm.yaml配置文件中设置的storm.local.dir目录具有写权限。</p>
<p>&nbsp;&nbsp;Storm后台进程被启动时,将在Storm安装目录下的logs/子目录下生成各个进程的日志文件。</p>
<p>&nbsp;&nbsp;Storm UI必须和Storm Nimbus部署在同一台机器上,否则UI无法正常工作,因为UI进程会检查本机是否存在Nimbus链接。</p>
<h3 id="常用命令"><a href="#常用命令" class="headerlink" title="常用命令"></a>常用命令</h3><table>
<thead>
<tr>
<th>命令描述</th>
<th>格式</th>
<th>例子</th>
</tr>
</thead>
<tbody><tr>
<td>启动Nimbus</td>
<td>storm nimbus</td>
<td>storm nimbus</td>
</tr>
<tr>
<td>启动Supervisor</td>
<td>storm supervisor</td>
<td>storm supervisor</td>
</tr>
<tr>
<td>启动UI</td>
<td>storm ui</td>
<td>storm ui</td>
</tr>
</tbody></table>
<h4 id="提交Topologies"><a href="#提交Topologies" class="headerlink" title="提交Topologies"></a>提交Topologies</h4><p><strong>格式</strong> </p>
<p>storm jar 【jar路径】 【拓扑包名.拓扑类名】【stormIP地址】【storm端口】【拓扑名称】【参数】</p>
<p><strong>Example</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">storm jar &#x2F;home&#x2F;storm&#x2F;hello.jar</span><br><span class="line">storm.hello.WordCountTopology wordcountTop</span><br><span class="line">提交hello.jar到远程集群,并启动wordcountTop拓扑</span><br></pre></td></tr></table></figure>
<h4 id="停止Topologies"><a href="#停止Topologies" class="headerlink" title="停止Topologies"></a>停止Topologies</h4><p><strong>格式</strong></p>
<p>storm kill [拓扑名称]</p>
<p><strong>Example</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">storm kill wordcountTop</span><br></pre></td></tr></table></figure>
<h3 id="API"><a href="#API" class="headerlink" title="API"></a>API</h3><h4 id="Spouts"><a href="#Spouts" class="headerlink" title="Spouts"></a>Spouts</h4><p>&nbsp;&nbsp; Spout是Stream的消息产生源， Spout组件的实现可以通过继承BaseRichSpout类或者其他*Spout类来完成，也可以通过实现IRichSpout接口来实现。</p>
<p><strong>open</strong></p>
<p>&nbsp;&nbsp;当一个Task被初始化的时候会调用open()。一般都会在此方法中对发送Tuple的对象SpoutOutputCollector和配置对象TopologyContext初始化。</p>
<p><strong>getComponentConfiguration</strong></p>
<p>&nbsp;&nbsp;此方法用于声明针对当前组件的特殊的Configuration配置。</p>
<p><strong>nextTuple</strong></p>
<p>&nbsp;&nbsp;这是Spout类中最重要的一个方法。发射一个Tuple到Topology都是通过这个方法来实现的。</p>
<p><strong>declareOutputFields</strong></p>
<p>&nbsp;&nbsp;此方法用于声明当前Spout的Tuple发送流。Stream的定义是通过OutputFieldsDeclare.declare方法完成的,其中的参数包括了发送的Fields。</p>
<p>&nbsp;&nbsp;另外，除了上述几个方法之外，还有ack、fail和close方法等。</p>
<p>&nbsp;&nbsp;Storm在监测到一个Tuple被成功处理之后会调用ack方法，处理失败会调用fail方法。这两个方法在BaseRichSpout等类中已经被隐式的实现了。</p>
<h4 id="Bolts"><a href="#Bolts" class="headerlink" title="Bolts"></a>Bolts</h4><p>&nbsp;&nbsp; Bolt类接收由Spout或者其他上游Bolt类发来的Tuple，对其进行处理。Bolt组件的实现可以通过继承BasicRichBolt类或者IRichBolt接口来完成。</p>
<p><strong>prepare</strong></p>
<p>&nbsp;&nbsp;此方法与Spouts的open方法类似,为Bolt提供了OutputCollector,用来从Bolt中发射Tuple。Bolt中Tuple的发射可以在prepare中、execute中、cleanup等方法中进行,一般都是在execute中。</p>
<p><strong>getComponentConfiguration</strong></p>
<p>&nbsp;&nbsp;与Spouts类似。</p>
<p><strong>execute</strong></p>
<p>&nbsp;&nbsp;  这是Bolt中最关键的一个方法，对于Tuple的处理都可以放到此方法中进行。具体的发送也是在execute中通过调用emit方法来完成的。</p>
<p>&nbsp;&nbsp;emit有两种情况，一种是emit方法中有两个参数，另一个种是有一个参数。</p>
<ol>
<li>emit有一个参数：此唯一的参数是发送到下游Bolt的Tuple，此时，由上游发来的旧的Tuple在此隔断，新的Tuple和旧的Tuple不再属于同一棵Tuple树。新的Tuple另起一个新的Tuple树。</li>
<li>emit有两个参数：第一个参数是旧的Tuple的输入流，第二个参数是发往下游Bolt的新的Tuple流。此时，新的Tuple和旧的Tuple是仍然属于同一棵Tuple树，即，如果下游的Bolt处理Tuple失败，则会向上传递到当前Bolt，当前Bolt根据旧的Tuple流继续往上游传递，申请重发失败的Tuple。保证Tuple处理的可靠性。</li>
</ol>
<p><strong>declareOutputFields</strong></p>
<p>&nbsp;&nbsp;用于声明当前Bolt发送的Tuple中包含的字段。</p>
<h4 id="Topology-Example"><a href="#Topology-Example" class="headerlink" title="Topology Example"></a>Topology Example</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RandomWordSpout</span> <span class="keyword">extends</span> <span class="title">BaseRichSpout</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 初始化数据字典</span></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> String[] words = &#123; <span class="string">&quot;java&quot;</span>, <span class="string">&quot;c&quot;</span>, <span class="string">&quot;c++&quot;</span>, <span class="string">&quot;c#&quot;</span>, <span class="string">&quot;python&quot;</span>, <span class="string">&quot;go&quot;</span>, <span class="string">&quot;javascript&quot;</span>,</span><br><span class="line">			<span class="string">&quot;swift&quot;</span> &#125;;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">private</span> SpoutOutputCollector collector;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">nextTuple</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		Random random = <span class="keyword">new</span> Random();</span><br><span class="line">		<span class="comment">// 获取随机的单词</span></span><br><span class="line">		String word = words[random.nextInt(words.length)];</span><br><span class="line">		<span class="comment">// 发射消息</span></span><br><span class="line">		<span class="keyword">this</span>.collector.emit(<span class="keyword">new</span> Values(word));</span><br><span class="line">		<span class="comment">// 休息2秒</span></span><br><span class="line">		Utils.sleep(<span class="number">2000</span>);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">open</span><span class="params">(Map arg0, TopologyContext arg1, SpoutOutputCollector collector)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">this</span>.collector = collector;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">declareOutputFields</span><span class="params">(OutputFieldsDeclarer declarer)</span> </span>&#123;</span><br><span class="line">		<span class="comment">// 声明字段名</span></span><br><span class="line">		declarer.declare(<span class="keyword">new</span> Fields(<span class="string">&quot;initName&quot;</span>));</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UpperBolt</span> <span class="keyword">extends</span> <span class="title">BaseBasicBolt</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">(Tuple tuple, BasicOutputCollector collector)</span> </span>&#123;</span><br><span class="line">		<span class="comment">// 获得上个bolt传入的initName</span></span><br><span class="line">		String initName = tuple.getString(<span class="number">0</span>);</span><br><span class="line">		<span class="comment">// 将initName转为大写</span></span><br><span class="line">		String upperCase = initName.toUpperCase();</span><br><span class="line">		<span class="comment">// 发射消息</span></span><br><span class="line">		collector.emit(<span class="keyword">new</span> Values(upperCase));</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">declareOutputFields</span><span class="params">(OutputFieldsDeclarer declarer)</span> </span>&#123;</span><br><span class="line">		declarer.declare(<span class="keyword">new</span> Fields(<span class="string">&quot;upperName&quot;</span>));</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">PrefixBolt</span> <span class="keyword">extends</span> <span class="title">BaseBasicBolt</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">private</span> FileWriter fileWriter;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">prepare</span><span class="params">(Map stormConf, TopologyContext context)</span> </span>&#123;</span><br><span class="line">		<span class="comment">// 初始化fileWriter</span></span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			<span class="keyword">this</span>.fileWriter = <span class="keyword">new</span> FileWriter(<span class="string">&quot;/home/storm/output/&quot;</span> + UUID.randomUUID());</span><br><span class="line">		&#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">			e.printStackTrace();</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">(Tuple tuple, BasicOutputCollector collector)</span> </span>&#123;</span><br><span class="line">		String upperName = tuple.getString(<span class="number">0</span>);</span><br><span class="line">		<span class="comment">// 添加前缀</span></span><br><span class="line">		String finalName = <span class="string">&quot;hello-&quot;</span> + upperName;</span><br><span class="line">		<span class="comment">// write</span></span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			<span class="keyword">this</span>.fileWriter.write(finalName);</span><br><span class="line">			<span class="keyword">this</span>.fileWriter.write(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line">			<span class="keyword">this</span>.fileWriter.flush();</span><br><span class="line">		&#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">			e.printStackTrace();</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">declareOutputFields</span><span class="params">(OutputFieldsDeclarer declarer)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TopologyMain</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		TopologyBuilder topologyBuilder = <span class="keyword">new</span> TopologyBuilder();</span><br><span class="line">		<span class="comment">// 设置Spout</span></span><br><span class="line">		topologyBuilder.setSpout(<span class="string">&quot;randomWordSpout&quot;</span>, <span class="keyword">new</span> RandomWordSpout());</span><br><span class="line">		<span class="comment">// 设置Bolt</span></span><br><span class="line">		topologyBuilder.setBolt(<span class="string">&quot;upperBolt&quot;</span>, <span class="keyword">new</span> UpperBolt()).shuffleGrouping(<span class="string">&quot;randomWordSpout&quot;</span>);</span><br><span class="line">		topologyBuilder.setBolt(<span class="string">&quot;prefixBolt&quot;</span>, <span class="keyword">new</span> PrefixBolt()).shuffleGrouping(<span class="string">&quot;upperBolt&quot;</span>);</span><br><span class="line"></span><br><span class="line">		Config config = <span class="keyword">new</span> Config();</span><br><span class="line">		<span class="comment">// 设置Workers数量</span></span><br><span class="line">		config.setNumWorkers(<span class="number">4</span>);</span><br><span class="line">		config.setDebug(<span class="keyword">true</span>);</span><br><span class="line">		<span class="comment">// 提交Topology</span></span><br><span class="line">		StormSubmitter.submitTopology(<span class="string">&quot;randomTopo&quot;</span>, config, topologyBuilder.createTopology());</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
    </div>

    
    
    

    <footer class="post-footer">
          <div class="post-tags">
              <a href="/yuwanzi.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/" rel="tag"># 大数据</a>
              <a href="/yuwanzi.io/tags/Storm/" rel="tag"># Storm</a>
          </div>

        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/yuwanzi.io/2016/07/18/2016-07-18-Hadoop05-Hive/" rel="prev" title="Hadoop学习笔记(5)-Hive">
                  <i class="fa fa-chevron-left"></i> Hadoop学习笔记(5)-Hive
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/yuwanzi.io/2016/07/29/2016-07-29-Spring-boot-autoconfigure/" rel="next" title="浅谈Spring Boot自动配置的运作原理">
                  浅谈Spring Boot自动配置的运作原理 <i class="fa fa-chevron-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>







<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      const activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      const commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>
</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2021</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">玉丸子</span>
</div>
  <div class="powered-by">Erstellt mit  <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" class="theme-link" rel="noopener" target="_blank">NexT.Muse</a>
  </div>

    </div>
  </footer>

  
  <script src="//cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js"></script>
<script src="/yuwanzi.io/js/utils.js"></script><script src="/yuwanzi.io/js/motion.js"></script><script src="/yuwanzi.io/js/schemes/muse.js"></script><script src="/yuwanzi.io/js/next-boot.js"></script>

  






  





</body>
</html>
